/**
* \file	CodeGenerator.cpp
* \author Caio Arce Nishibe (caionishibe@gmail.com)
* \date	August, 2014
* \brief C++ Code Generator Implementation.
*/
#include "CodeGenerator.h"


CodeGenerator::CodeGenerator(string filename)
{
	this->generatedCodeFile.open(filename.c_str());

	if (this->generatedCodeFile.is_open() == false)
	{
		cout << "Unable to open file!" << endl;
		exit(1);
	}
}

CodeGenerator::~CodeGenerator()
{
	this->generatedCodeFile.close();
}

void CodeGenerator::VisitProgram(ASTNode *node)
{
	this->generatedCodeFile << "/* Generated by Agents Language Compiler */" << endl;
	this->generatedCodeFile << endl;
	//define to avoid secure warnings
	this->generatedCodeFile << "#define _CRT_SECURE_NO_WARNINGS" << endl;

	//includes
	this->generatedCodeFile << "#include <stdio.h>" << endl;
	this->generatedCodeFile << "#include <cstdlib>" << endl;
	this->generatedCodeFile << "#include <stddef.h>" << endl;
	this->generatedCodeFile << "#include <string>" << endl;
	this->generatedCodeFile << "#include <boost\\asio.hpp>" << endl;
	this->generatedCodeFile << "#include <boost\\algorithm\\string.hpp>" << endl;
	this->generatedCodeFile << "#include <Message.h>" << endl;
	this->generatedCodeFile << "#include <vector>" << endl;

	this->generatedCodeFile << "#include \"AgentsLanguageAux.h\"" << endl;
	this->generatedCodeFile << "using namespace AgentsLanguage;" << endl;
	this->generatedCodeFile << "using namespace std;" << endl;
	this->generatedCodeFile << "using boost::asio::ip::tcp;" << endl;



	this->generatedCodeFile << endl;




	bool firstVariable = true;


	//program name and variable declarations
	ASTNode *child;
	for (child = node->children; child != NULL && child->kind != RULES; child = child->sibling)
	{
		if (child->kind == DECL && firstVariable)
		{
			this->generatedCodeFile << endl;
			this->generatedCodeFile << "/* Variables */" << endl;
			firstVariable = false;
		}

		child->Accept(this);
	}




	//main

	if (child != NULL)
	{
		this->generatedCodeFile << endl;
		this->generatedCodeFile << "int main (int argc, char *argv[]) \n{" << endl;

		//parsing args
		this->generatedCodeFile << "\t" << "int _portNumber = 0;" << endl;
		this->generatedCodeFile << "\t" << "int _leftMotorHandler = 0;" << endl;
		this->generatedCodeFile << "\t" << "int _rightMotorHandler = 0;" << endl;
		this->generatedCodeFile << "\t" << "int _proximitySensorHandler = 0;" << endl;
		this->generatedCodeFile << "\t" << "int _rightVisionSensorHandler = 0;" << endl;
		this->generatedCodeFile << "\t" << "int _middleVisionSensorHandler = 0;" << endl;
		this->generatedCodeFile << "\t" << "int _leftVisionSensorHandler = 0;" << endl;


		this->generatedCodeFile << "\t" << "if (argc >= 8)" << endl;
		this->generatedCodeFile << "\t" << "{" << endl;
		this->generatedCodeFile << "\t\t" << "_portNumber = atoi(argv[1]);" << endl;
		this->generatedCodeFile << "\t\t" << "_leftMotorHandler = atoi(argv[2]);" << endl;
		this->generatedCodeFile << "\t\t" << "_rightMotorHandler = atoi(argv[3]);" << endl;
		this->generatedCodeFile << "\t\t" << "_proximitySensorHandler = atoi(argv[4]);" << endl;
		this->generatedCodeFile << "\t\t" << "_rightVisionSensorHandler = atoi(argv[5]);" << endl;
		this->generatedCodeFile << "\t\t" << "_middleVisionSensorHandler = atoi(argv[6]);" << endl;
		this->generatedCodeFile << "\t\t" << "_leftVisionSensorHandler = atoi(argv[7]);" << endl;
		this->generatedCodeFile << "\t" << "}" << endl;
		this->generatedCodeFile << "\t" << "else" << endl;
		this->generatedCodeFile << "\t" << "{" << endl;
		this->generatedCodeFile << "\t\t" << "printf(\"Expecting following arguments: portNumber leftMotorHandler rightMotorHandler proximitySensorHandler rightVisionSensorHandler middleVisionSensorHandler leftVisionSensorHandler\\n\");" << endl;
		this->generatedCodeFile << "\t\t" << "return 0;" << endl;
		this->generatedCodeFile << "\t" << "}" << endl;
		this->generatedCodeFile << endl;

		//message server connection
		this->generatedCodeFile << "\t" << "boost::asio::io_service ioService;" << endl;
		this->generatedCodeFile << "\t" << "tcp::resolver resolver(ioService);" << endl;
		this->generatedCodeFile << "\t" << "tcp::resolver::query query(tcp::v4(), \"localhost\", \"1313\");" << endl;
		this->generatedCodeFile << "\t" << "tcp::resolver::iterator iterator = resolver.resolve(query);" << endl;

		this->generatedCodeFile << "\t" << "tcp::socket s(ioService);" << endl;
		this->generatedCodeFile << "\t" << "boost::asio::connect(s,iterator);" << endl;

		//client initialization
		this->generatedCodeFile << "\t" << "int _clientID = simxStart((simxChar*)\"127.0.0.1\", _portNumber, true, true, 2000, 5);" << endl;
		this->generatedCodeFile << "\t" << "if (_clientID != -1)" << endl;
		this->generatedCodeFile << "\t" << "{" << endl;

		//message to verify if there is any messages for me
		this->generatedCodeFile << "\t\t" << "string msgAnyMessage = _program_name + \" server\";" << endl;
		this->generatedCodeFile << "\t\t" << "MessageHeader header(msgAnyMessage.length() + 5, MessageType::ANY_MESSAGE_FOR_ME);" << endl;
		this->generatedCodeFile << "\t\t" << "Message anyMessage(header, msgAnyMessage);" << endl;
		this->generatedCodeFile << "\t\t" << "string msgAnyMessageBuffer = anyMessage.GetMessageBuffer();" << endl;

		//main loop
		this->generatedCodeFile << "\t\t" << "while(simxGetConnectionId(_clientID) != -1)" << endl;
		this->generatedCodeFile << "\t\t" << "{" << endl;

		this->generatedCodeFile << "\t\t\t" << "//main loop" << endl;

		this->generatedCodeFile << "/* begin user generated code */" << endl;

		//rules
		child->Accept(this);

		this->generatedCodeFile << "/* end user generated code */" << endl;

		//check if there is any message for me
		this->generatedCodeFile << endl;
		this->generatedCodeFile << "\t" << "boost::asio::write(s, boost::asio::buffer(msgAnyMessageBuffer.c_str(), msgAnyMessageBuffer.length()));" << endl;
		this->generatedCodeFile << "\t" << "vector<char> replyHeader(5);" << endl;
		this->generatedCodeFile << "\t" << "boost::asio::read(s, boost::asio::buffer(replyHeader));" << endl;
		this->generatedCodeFile << "\t" << "int messageLength = 0;" << endl;
		this->generatedCodeFile << "\t" << "messageLength |= replyHeader[0] << 24;" << endl;
		this->generatedCodeFile << "\t" << "messageLength |= replyHeader[1] << 16;" << endl;
		this->generatedCodeFile << "\t" << "messageLength |= replyHeader[2] << 8;" << endl;
		this->generatedCodeFile << "\t" << "messageLength |= replyHeader[3];" << endl;

		this->generatedCodeFile << "\t" << "int msgType = replyHeader[4];" << endl;

		this->generatedCodeFile << "\t" << "if(msgType != MessageType::NO_MESSAGES_FOR_YOU)" << endl;
		this->generatedCodeFile << "\t" << "{" << endl;
		this->generatedCodeFile << "\t" << "vector<char> replyMessage(messageLength - replyHeader.size());" << endl;
		this->generatedCodeFile << "\t" << "boost::asio::read(s, boost::asio::buffer(replyMessage));" << endl;
		this->generatedCodeFile << "\t" << "string strMsg(replyMessage.data(), replyMessage.size());" << endl;
		this->generatedCodeFile << "\t" << "vector<string> subStrings;" << endl;
		this->generatedCodeFile << "\t" << "boost::split(subStrings, strMsg, boost::is_any_of(\" \"));" << endl;

		this->generatedCodeFile << "\t" << "if(msgType == MessageType::SEND_INFO)" << endl;
		this->generatedCodeFile << "\t" << "{" << endl;
		this->generatedCodeFile << "\t" << "string varName = subStrings[2];" << endl;
		this->generatedCodeFile << "\t" << "if(varName == \"semaphorePosition\")" << endl;
		this->generatedCodeFile << "\t" << "{" << endl;
		this->generatedCodeFile << "\t" << "semaphorePosition = atof(subStrings[3].c_str());" << endl;
		this->generatedCodeFile << "\t" << "}" << endl;
		this->generatedCodeFile << "\t" << "}" << endl;
		this->generatedCodeFile << "\t" << "}" << endl;
		this->generatedCodeFile << endl;

		this->generatedCodeFile << "\t\t\t" << "extApi_sleepMs(5);" << endl;
		this->generatedCodeFile << "\t\t" << "}" << endl;

		this->generatedCodeFile << "\t\t" << "simxFinish(_clientID);" << endl;
		this->generatedCodeFile << "\t" << "}" << endl;



		this->generatedCodeFile << endl;
		this->generatedCodeFile << "\n\t return 0;\n}" << endl << endl;
	}
}

void CodeGenerator::VisitName(ASTNode *node)
{
	//program name
	this->generatedCodeFile << "/* Program Name */" << endl;
	this->generatedCodeFile << "string _program_name = \"";
	node->children->Accept(this);
	this->generatedCodeFile << "\";" << endl;

	//program version
	if (node->children->sibling != NULL)
	{
		this->generatedCodeFile << "/* Program Version */" << endl;
		this->generatedCodeFile << "const double _program_version = ";
		node->children->sibling->Accept(this);
		this->generatedCodeFile << ";" << endl;
	}

}

void CodeGenerator::VisitVariable(ASTNode *node)
{
	this->generatedCodeFile << "double ";
	node->children->Accept(this);
	this->generatedCodeFile << " = ";
	node->children->sibling->Accept(this);
	this->generatedCodeFile << ";" << endl;
}

void CodeGenerator::VisitRules(ASTNode *node)
{
	//rule_set
	node->children->Accept(this);
}

void  CodeGenerator::VisitRulesSet(ASTNode *node)
{
	ASTNode *rule;
	for (rule = node->children; rule != NULL; rule = rule->sibling)
	{
		this->generatedCodeFile << endl;
		this->generatedCodeFile << "\t/* Rule */" << endl;
		rule->Accept(this);
	}
}

void CodeGenerator::VisitRule(ASTNode *node)
{
	//condition	
	this->generatedCodeFile << "\tif(";
	ASTNode *condition = node->children;
	condition->Accept(this);
	this->generatedCodeFile << ")" << endl;

	this->generatedCodeFile << "\t{" << endl;
	//action
	ASTNode *action1 = condition->sibling;
	action1->Accept(this);
	this->generatedCodeFile << "\t}" << endl;

	//action
	ASTNode *action2 = action1->sibling;
	if (action2 != NULL)
	{
		this->generatedCodeFile << "\telse" << endl;
		this->generatedCodeFile << "\t{" << endl;
		action2->Accept(this);
		this->generatedCodeFile << "\t}" << endl;
	}
}

void CodeGenerator::VisitCondition(ASTNode *node)
{
	ASTNode *child;


	for (child = node->children; child != NULL; child = child->sibling)
	{
		child->Accept(this);
	}
}

void CodeGenerator::VisitInternalCondition(ASTNode *node)
{
	ASTNode *child;

	this->generatedCodeFile << "(";

	for (child = node->children; child != NULL; child = child->sibling)
	{
		if (child->kind == ARITH_EXPR && child->children->kind != NUMBER && child->children->kind != IDENTIFIER)
			this->generatedCodeFile << "(";
		child->Accept(this);
		if (child->kind == ARITH_EXPR && child->children->kind != NUMBER && child->children->kind != IDENTIFIER)
			this->generatedCodeFile << ")";
	}

	this->generatedCodeFile << ")";
}

void CodeGenerator::VisitComparationOperator(ASTNode *node)
{
	CompOp op;
	node->value.GetValue(COMPARATOR, &op);

	switch (op)
	{
	case EQUAL:
		this->generatedCodeFile << " == ";
		break;
	case DIFFERENT:
		this->generatedCodeFile << " != ";
		break;
	case LESS:
		this->generatedCodeFile << " < ";
		break;
	case LESS_EQUAL:
		this->generatedCodeFile << " <= ";
		break;
	case GREATER:
		this->generatedCodeFile << " > ";
		break;
	case GREATER_EQUAL:
		this->generatedCodeFile << " >= ";
		break;
	}
}

void CodeGenerator::VisitLogicOperator(ASTNode *node)
{
	LogicOp op;
	node->value.GetValue(LOGIC, &op);

	switch (op)
	{
	case NOT:
		this->generatedCodeFile << "~";
		break;
	case AND:
		this->generatedCodeFile << " && ";
		break;
	case OR:
		this->generatedCodeFile << " || ";
		break;
	}
}

void CodeGenerator::VisitIdentifier(ASTNode *node)
{
	string identifier;
	node->value.GetValue(STRING, &identifier);

	this->generatedCodeFile << identifier;

}

void CodeGenerator::VisitNumber(ASTNode *node)
{
	double number;
	node->value.GetValue(DOUBLE, &number);

	this->generatedCodeFile.precision(numeric_limits<double>::digits10);
	//this->generatedCodeFile << fixed << number;
	this->generatedCodeFile << number;
}

void CodeGenerator::VisitArithmeticOperator(ASTNode *node)
{
	ArithOp op;
	node->value.GetValue(ARITHMETIC, &op);

	string stringOp;

	switch (op)
	{
	case SUM:
		stringOp = " + ";
		break;
	case SUBTRACTION:
		stringOp = " - ";
		break;
	case MULTIPLICATION:
		stringOp = " * ";
		break;
	case DIVISION:
		stringOp = " / ";
		break;
	}

	this->generatedCodeFile << stringOp;
}

void CodeGenerator::VisitArithmeticExpression(ASTNode *node)
{
	ASTNode *child;

	for (child = node->children; child != NULL; child = child->sibling)
	{
		if (child->kind == ARITH_EXPR && child->children->kind != NUMBER && child->children->kind != IDENTIFIER)
			this->generatedCodeFile << "(";

		child->Accept(this);

		if (child->kind == ARITH_EXPR && child->children->kind != NUMBER && child->children->kind != IDENTIFIER)
			this->generatedCodeFile << ")";
	}

}

void CodeGenerator::VisitAction(ASTNode * node)
{
	node->children->Accept(this);
}

void CodeGenerator::VisitCommand(ASTNode * node)
{
	ASTNode *child;
	for (child = node->children; child != NULL; child = child->sibling)
	{
		child->Accept(this);
	}

}

void CodeGenerator::VisitCmd(ASTNode *node)
{
	node->children->Accept(this);

}

void CodeGenerator::VisitAssignment(ASTNode *node)
{
	ASTNode *ident = node->children;
	ASTNode *number = ident->sibling;

	if (ident != NULL && number != NULL)
	{

		this->generatedCodeFile << "\t\t";
		ident->Accept(this);
		this->generatedCodeFile << " = ";
		number->Accept(this);
		this->generatedCodeFile << ";" << endl;
	}
}

void CodeGenerator::VisitReadSensor(ASTNode *node)
{
	string identifier;
	node->children->value.GetValue(STRING, &identifier);

	if (identifier == "rightSensor")
		this->generatedCodeFile << "\t\t\t" << "rightSensor = Aux::readVisionSensor(_clientID, _rightVisionSensorHandler);" << endl;
	else if (identifier == "middleSensor")
		this->generatedCodeFile << "\t\t\t" << "middleSensor = Aux::readVisionSensor(_clientID, _middleVisionSensorHandler);" << endl;
	else if (identifier == "leftSensor")
		this->generatedCodeFile << "\t\t\t" << "leftSensor = Aux::readVisionSensor(_clientID, _leftVisionSensorHandler);" << endl;
	else if (identifier == "proximitySensor")
		this->generatedCodeFile << "\t\t\t" << "proximitySensor = Aux::readProximitySensor(_clientID, _proximitySensorHandler);" << endl;

}

void CodeGenerator::VisitWriteActuator(ASTNode *node)
{
	ASTNode * ident = node->children;
	ASTNode * number = ident->sibling;


	if (ident != NULL && number != NULL)
	{
		string identStr;
		ident->value.GetValue(Type::STRING, &identStr);

		if (identStr == "rightMotor")
			this->generatedCodeFile << "\t\t\t" << "Aux::setMotorTargetVelocity(_clientID, _rightMotorHandler, rightMotorSpeed);" << endl;
		else if (identStr == "leftMotor")
			this->generatedCodeFile << "\t\t\t" << "Aux::setMotorTargetVelocity(_clientID, _leftMotorHandler, leftMotorSpeed);" << endl;

	}
}

void CodeGenerator::VisitMessage(ASTNode *node)
{
	ASTNode * name = node->children;
	ASTNode * nameIdent = name->children;
	string nameStr;
	nameIdent->value.GetValue(Type::STRING, &nameStr);

	ASTNode * variableName = name->sibling;
	string strVarName;
	variableName->value.GetValue(Type::STRING, &strVarName);

	this->generatedCodeFile << "\t" << "string msgReq" << nameStr << strVarName << "= _program_name + \" " << nameStr << " " << strVarName << "\";" << endl;
	this->generatedCodeFile << "\t" << "MessageHeader headerReq" << nameStr << strVarName << "(5 + msgReq" << nameStr << strVarName << ".length(), MessageType::REQUEST_INFO);" << endl;
	this->generatedCodeFile << "\t" << "Message request" << nameStr << strVarName << "(headerReq" << nameStr << strVarName << ", msgReq" << nameStr << strVarName << ");" << endl;
	this->generatedCodeFile << "\t" << "string buffReq" << nameStr << strVarName << " = request" << nameStr << strVarName << ".GetMessageBuffer();" << endl;
	this->generatedCodeFile << "\t" << "boost::asio::write(s, boost::asio::buffer(buffReq" << nameStr << strVarName << ".c_str(), buffReq" << nameStr << strVarName << ".length()));" << endl;
}